package com.yanxi.user.web.common.util;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

import java.util.concurrent.TimeUnit;

public class ManagedChannelUtils {

    /**
     * 初始化通道 ManagedChannel 直接就把需要配置中心的地址 端口号写死在代码中。
     *
     * 为什么直接把配置写死在代码中，因为application.yml已经不存在了，所以需要将固定连接的配置写死在代码中
     * 我在想是不是还有其他方法来记载这些固定的配置，也可以实现动态加载配置的效果。
     *
     * 每句的注释：
     * forAddress() 指定 gRPC 服务端的主机和端口
     * usePlaintext() 使用明文传输，实际环境建议使用 TLS
     * enableRetry() 启用 gRPC 的重试机制
     * maxRetryAttempts() 配置最大重试次数5
     * keepAliveTime() 保持活动连接的时间 5分钟
     *
     * @return
     */
    private static ManagedChannel initClientServe(String url, String port) {
        return ManagedChannelBuilder
                .forAddress(url, Integer.parseInt(port))
                .usePlaintext()
                .enableRetry()
                .maxRetryAttempts(5)
                .keepAliveTime(5, TimeUnit.SECONDS)
                .build();
    }

    /**
     * 获取配置好的通道，并在使用完毕后自动关闭
     *
     * @return
     */
    public static void runWithManagedChannel(String url, String port, ChannelConsumer consumer) {
        try (ManagedChannelWrapper channelWrapper = new ManagedChannelWrapper(initClientServe(url, port))) {
            ManagedChannel channel = channelWrapper.getChannel();
            consumer.accept(channel);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 包装类，实现 AutoCloseable 接口
     */
    public static class ManagedChannelWrapper implements AutoCloseable {
        private final ManagedChannel channel;

        public ManagedChannelWrapper(ManagedChannel channel) {
            this.channel = channel;
        }

        @Override
        public void close() {
            if (channel != null && !channel.isShutdown()) {
                channel.shutdown();
            }
        }

        public ManagedChannel getChannel() {
            return channel;
        }
    }

    /**
     * 函数式接口，用于接收 ManagedChannel 并执行相关操作
     */
    @FunctionalInterface
    public interface ChannelConsumer {
        void accept(ManagedChannel channel) throws Exception;
    }

}

